package scan 
{
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	import api.com.adobe.crypto.MD5;
	
	/**
	 * Frame data keeper
	 */
	public class Frame extends Space
	{
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		public static const ALPHA_LIMIT:Number = 0.01;
		public static const EMPTY_CHECKSUMM:String = "00000000000000000000000000000000";
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		private static var emptyPoint:Point = new Point();
		
		private var _index:int;
		
		private var _rawBitmap:BitmapData;
		private var _cropBitmap:BitmapData;
		
		private var _cropX:int;
		private var _cropY:int;
		
		private var _checksumm:String = EMPTY_CHECKSUMM;
		
		private var _fileName:String = "";
		private var _doubler:Frame;
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		/**
		 * Constructor
		 * @param	index			frame index
		 * @param	bitmap			frame bitmap data
		 * @param	scanForAlpha	scan bitmap for detecting alpha bounds
		 * @param	getChecksumm	get bitmap hash for detecting equal frames
		 */
		public function Frame(index:int, bitmap:BitmapData, scanForAlpha:Boolean=false, getChecksumm:Boolean=false)
		{
			super(0, 0, bitmap.width, bitmap.height);
			
			_index = index;
			_rawBitmap = bitmap;
			
			if (scanForAlpha)
			{
				scan(bitmap);
			}
			else
			{
				_cropX = 0;
				_cropY = 0;
				
				_cropBitmap = bitmap;
			}
			
			if (frameIsEmpty)
			{
				super.width = 0;
				super.height = 0;
			}
			else
			{
				super.width = _cropBitmap ? _cropBitmap.width : _rawBitmap.width;
				super.height = _cropBitmap ? _cropBitmap.height : _rawBitmap.height;
				
				if (getChecksumm && _cropBitmap)
				{
					var pixels:ByteArray = _cropBitmap.getPixels(new Rectangle(0, 0, _cropBitmap.width, _cropBitmap.height));
					pixels.position = 0;
					
					_checksumm = MD5.hashBytes(pixels);
				}
			}
		}
		
		/**
		 * Width (can't reset bitmap width)
		 */
		override public function set width(value:int):void
		{
		}
		
		/**
		 * Height (can't reset bitmap height)
		 */
		override public function set height(value:int):void
		{
		}
		
		/**
		 * Frame index
		 */
		public function get index():int
		{
			return _index;
		}
		
		/**
		 * Alpha cropped bitmap left offset
		 */
		public function get cropX():int
		{
			return _cropX;
		}
		
		/**
		 * Alpha cropped bitmap top offset
		 */
		public function get cropY():int
		{
			return _cropY;
		}
		
		/**
		 * Alpha cropped bitmap width
		 */
		public function get cropWidth():int
		{
			return _cropBitmap ? int(_cropBitmap.width) : 0;
		}
		
		/**
		 * Alpha cropped bitmap height
		 */
		public function get cropHeight():int
		{
			return _cropBitmap ? int(_cropBitmap.height) : 0;
		}
		
		/**
		 * Get frame bitmap, or his fragment
		 * @param	rc	region of frame bitmap to return, or null if alpha cropped bitmap required
		 * @return		specified fragment of frame bitmap
		 */
		public function getBitmap(rc:Rectangle=null):BitmapData
		{
			if (rc)
			{
				if (rc.x == 0 && rc.y == 0 && rc.width == _rawBitmap.width && rc.height == _rawBitmap.height)
					return _rawBitmap;
				
				var res:BitmapData = new BitmapData(rc.width, rc.height, true, 0x000000);
				res.copyPixels(_rawBitmap, rc, emptyPoint, null, null, true);
				return res;
			}
			
			return _cropBitmap;
		}
		
		/**
		 * MD5 hash for frame bitmap
		 */
		public function get checksumm():String
		{
			return _checksumm;
		}
		
		/**
		 * Flag, indicates when frame is empty
		 */
		public function get frameIsEmpty():Boolean
		{
			return _cropBitmap == null;
		}
		
		/**
		 * Name of the file in which to store the frame
		 */
		public function set fileName(value :String):void
		{
			if (value == _fileName)
				return;
			
			_fileName = value;
		}
		
		public function get fileName():String
		{
			return _fileName;
		}
		
		/**
		 * Identical frame-doubler
		 */
		public function set doubler(value:Frame):void
		{
			if (_checksumm == EMPTY_CHECKSUMM || value == this)
				value = null;
			
			if (value == _doubler)
				return;
			
			_doubler = value;
		}
		
		public function get doubler():Frame
		{
			return _doubler;
		}
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		/**
		 * Scan bitmap for detect and crop alpha bounds
		 * @param	raw		bitmap for scan
		 */
		private function scan(raw:BitmapData):void
		{
			var pixels:ByteArray = raw.getPixels(new Rectangle(0, 0, raw.width, raw.height));
			pixels.position = 0;
			
			var l:int = raw.width;
			var r:int = 0;
			var t:int = raw.height;
			var b:int = 0;
			
			var isEmpty:Boolean = true;
			
			for (var h:int = 0; h < raw.height; h++)
			{
				for (var w:int = 0; w < raw.width; w++)
				{
					var currentPixel:uint = pixels.readUnsignedInt();
					
					var pixel:uint = currentPixel >>> 56;
					if (pixel > 0)
					{
						var alpha:Number = pixel / 255.0;
						if (alpha > ALPHA_LIMIT)
						{
							isEmpty = false;
							
							if (h < t) t = h;
							if (h > b) b = h;
							if (w < l) l = w;
							if (w > r) r = w;
						}
					}
				}
			}
			
			if (isEmpty)
			{
				_cropX = 0;
				_cropY = 0;
				
				_cropBitmap = null;
			}
			else
			{
				if (l > r || t > b)
					throw Error("Failed to crop alpha");
				
				if (l == 0 && r == raw.width && t == 0 && b == raw.height)
				{
					_cropX = 0;
					_cropY = 0;
					
					_cropBitmap = raw;
				}
				else
				{
					_cropX = l;
					_cropY = t;
					
					var rc:Rectangle = new Rectangle(l, t, r - l + 1, b - t + 1);
					_cropBitmap = new BitmapData(rc.width, rc.height, true, 0x00000000);
					_cropBitmap.copyPixels(raw, rc, emptyPoint, null, null, true);
				}
			}
		}
	}
}